%%% ====================================================================
%%% Support for XML Schema in Erlang
%%% 
%%% Copyright (C) 2006 Willem de Jong
%%%
%%% This program is free software; you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by 
%%% the Free Software Foundation; either version 2 of the License, or 
%%% (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful, but
%%% WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License 
%%% along with this program; if not, write to the Free Software 
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%%% USA
%%%
%%% Author contact: w.a.de.jong@gmail.com
%%% ====================================================================

%%% Version: 0.0.3

%%% This is the user interface for the Erlsom functions.

-module(erlsom).

%% user interface
-export([compile/1, compile/2, compile/3,
         compile_file/1, compile_file/2, compile_file/3,
         parse/2, parse_file/2,
         write/2,
         sax/3,
         write_hrl/2, write_hrl/3, write_hrl/4,
         add_file/3, add_xsd_model/1]).

-include("erlsom.hrl").
-include("erlsom_parse.hrl").


%%----------------------------------------------------------------------
%% Function: compile/3
%% Purpose: compile an XSD into a structure to be used by erlsom:parse().
%% Args: 
%%     XSD  = string(): the XSD.
%%     Prefix = string
%%     Namespaces = [#ns]
%%
%%     'Prefix' is prefixed to the record names in the 
%%     in the XSD. 
%%     'Namespaces' should include the URIs of imported namespaces;
%%     the purpose is to define the prefix for those.
%%
%% Returns: {ok, Model}, where Model is the internal structure, see 
%% xml2struct.erl
%%----------------------------------------------------------------------
compile(Xsd) ->
  compile(Xsd, "p").

compile(Xsd, Prefix) ->
  compile(Xsd, Prefix, []).

compile(Xsd, Prefix, Namespaces) ->
  erlsom_compile:compile(Xsd, Prefix, Namespaces).

compile_file(XsdFile) ->
  compile_file(XsdFile, "p").

compile_file(XsdFile, Prefix) ->
  compile_file(XsdFile, Prefix, []).

compile_file(XsdFile, Prefix, Namespaces) ->
  case file:read_file(XsdFile) of
    {ok, Bin} ->
      %% compile(binary_to_list(Bin), Namespaces);
      compile(erlsom_lib:toUnicode(Bin), Prefix, Namespaces);
    Error ->
      Error
  end.

%%----------------------------------------------------------------------
%% Function: parse/2
%% Purpose: translate an XML document that conforms to the XSD to a 
%%     structure of records. (The XSD is represented by the 'Model', the
%%     result of the translation of the XSD by erlsom:compile().)
%% Args: 
%%     Xml = string(): the Xml document.
%%     Model = the internal representation of the XSD; the result of
%%     erlsom:compile().
%%
%% Returns: {ok, Structure}, where Structure is a structure of records 
%%     that represent the XML document. See the documentation for the 
%%     mapping.
%%----------------------------------------------------------------------
parse(Xml, Model) ->
  erlsom_sax:parseDocument(Xml, #state{model=Model, namespaces=[]},
                           {erlsom_parse, xml2StructCallback}).

parse_file(File, Model) ->
  case file:read_file(File) of
    {ok, Bin} ->
      parse(erlsom_lib:toUnicode(Bin), Model);
      %% parse(binary_to_list(Bin), Model);
    Error ->
      Error
  end.

%%----------------------------------------------------------------------
%% Function: write/2
%% Purpose: translate a structure of records to an XML document. This is the 
%%     inverse of erlsom:parse(). The XML will conform to an XSD, provided
%%     that the input structure matches with this XSD.
%%     (The XSD is represented by the 'Model', the result of the translation 
%%     of the XSD by erlsom:compile().)
%% Args: 
%%     Struct: a structure or records that represents the XML document. See 
%%     the documentation for the mapping.
%%     Model = the internal representation of the XSD; the result of
%%     erlsom:compile().
%%
%% Returns: {ok, Doucument} where Document is an XML document (a string),
%%     or {error, ErrorMessage}.
%%----------------------------------------------------------------------
write(Struct, Model) ->
  erlsom_write:write(Struct, Model).


%%----------------------------------------------------------------------
%% Function: sax/3
%% Purpose: parse an XML document, using the org.xml.sax ContentHandler 
%%     interface [SAX].
%%
%% Args: 
%%     Xml = A list of integers that correspond with the characters in an XML 
%%     document. Can be either 1 byte characters or integers that correspond 
%%     to Unicode code points.
%%
%%     State - a term() that is passed to the EventFun. 
%%
%%     Eventfun - a fun() that is called by the parser whenever it has parsed 
%%     a bit of the Xml input. The function is called by the parser according
%%     to the Sax specification (see [SAX]).
%%
%%     EventFun should accept the following arguments:
%%     - Event, a tuple that describes the event, see erlsom_sax for a 
%%       description of the events
%%     - State - a term()
%%
%%     EventFun should return State, a term() that wil be passed back to the 
%%     next invocation of EventFun.
%%
%%  Returns: State 
%%     (i.e.: the result of the last invocation of the callback function)
%%----------------------------------------------------------------------
sax(Xml, State, EventFun) ->
  erlsom_sax:parseDocument(Xml, State, EventFun).


%%----------------------------------------------------------------------
%% Function: write_hrl/3
%% Purpose: write record definitions (a .hrl file) for an xsd.
%%
%% Args: 
%%     Xsd = the filename of the .xsd file
%%
%%     Namespaces = [#ns]
%%
%%     'Namespaces' should include the URIs of all namespaces used
%%     in the XSD. 'Prefix' is prefixed to the record names.
%%
%%     Output = the name of the output file.
%%
%% Returns: ok, or an error if the file was not found. 
%%----------------------------------------------------------------------
write_hrl(Xsd, Output) ->
  write_hrl(Xsd, "p", [], Output).

write_hrl(Xsd, Prefix, Output) ->
  write_hrl(Xsd, Prefix, [], Output).

write_hrl(Xsd, Prefix, Namespaces, Output) ->
  case file:read_file(Xsd) of
    {ok, Bin} ->
      Hdr = erlsom_writeHrl:writeHrl(erlsom_lib:toUnicode(Bin), Prefix, Namespaces),
      file:write_file(Output, ucs:to_utf8(Hdr));
    Error ->
      Error
  end.


add_file(XsdFile, Namespaces, Model) ->
  case file:read_file(XsdFile) of
    {ok, Bin} ->
      erlsom_add:add(erlsom_lib:toUnicode(Bin), Namespaces, Model);
    Error ->
      Error
  end.
  
%% add the model for xml schema to a model.
%% We need a special function, since:
%% A - erlsom can't parse the schema for xml schema
%% B - even if it would be able to parse the schema for xml schema, 
%%     the output would be difficult to process.
%%
%% Expected to be used for parsing of WSDL files.
add_xsd_model(Model) ->
  erlsom_add:add_xsd_model(Model).
